package SampleHost;
import gnu.io.CommPortIdentifier;
import gnu.io.CommPort;
import gnu.io.SerialPort;
import gnu.io.PortInUseException;
import gnu.io.NoSuchPortException;
import gnu.io.UnsupportedCommOperationException;
import java.io.OutputStream;
import java.io.InputStream;
import java.io.IOException;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import java.util.TooManyListenersException;

public class DeviceLink implements SerialPortEventListener {

    public class PortNotSupportedException extends Exception {
        public PortNotSupportedException() {
            super("Port is not supported");
        }
    }

    public class AlreadyConnectedException extends Exception {
        public AlreadyConnectedException() {
            super("Port already connected");
        }
    }

    public class BlockTimeoutException extends Exception {

    }
    
    private static final String PORTOWNERNAME = "DEVICELINK";
    private static final int PORTOPENTIMEOUT = 2000;
    private static final int BAUDRATE = 115200;
    private static final int DATABITS = SerialPort.DATABITS_8;
    private static final int STOPBITS = SerialPort.STOPBITS_1;
    private static final int PARITY = SerialPort.PARITY_NONE;
    private static final int FLOWCONTROL = SerialPort.FLOWCONTROL_NONE;
    public static final int BLOCKFOREVER = 0;

    private SerialPort serialPort;
    private OutputStream outputStream;
    private InputStream inputStream;
    private SerialPortEvent serialPortEvent;

    public DeviceLink(String portName) throws PortInUseException,
                                              NoSuchPortException,
                                              PortNotSupportedException,
                                              UnsupportedCommOperationException,
                                              IOException,
                                              TooManyListenersException {
        CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);
        CommPort commPort = portIdentifier.open(PORTOWNERNAME, PORTOPENTIMEOUT);
        if (commPort instanceof SerialPort) {
            serialPort = (SerialPort)commPort;
            serialPort.setSerialPortParams(BAUDRATE,
                                           DATABITS,
                                           STOPBITS,
                                           PARITY);
            serialPort.setFlowControlMode(FLOWCONTROL);
            serialPort.addEventListener(this);
            outputStream = serialPort.getOutputStream();
            inputStream = serialPort.getInputStream();

            //serialPort.setDTR(false);
            serialPort.setRTS(true);
            boolean cd = serialPort.isCD();
            boolean cts = serialPort.isCTS();
            boolean dsr = serialPort.isDSR();
            boolean dtr = serialPort.isDTR();
            boolean ri = serialPort.isRI();

            boolean a = cd|cts;
        }
        else {
            throw new PortNotSupportedException();
        }
    }

    public void serialEvent(SerialPortEvent event) {
        synchronized(this) {
            System.out.println("serialEvent");
            serialPortEvent = event;
            notify();
        }
    }
    
    private void waitSerialPortEvent(int blockTimeout) throws Exception {
        synchronized(this) {
            serialPortEvent = null;
            //serialPort.notifyOnDataAvailable(true);
            serialPort.notifyOnBreakInterrupt(true);
            serialPort.notifyOnDSR(true);
            wait(blockTimeout);
            serialPort.notifyOnDataAvailable(false);
            if (null == serialPortEvent) {
                throw new BlockTimeoutException();
            }
        }
    }
    
    private void read(byte[] bytes, int blockTimeout) throws Exception {
        int counter = 0;
        while (inputStream.available() < bytes.length) {
            System.out.println("available: " + inputStream.available() + " " + counter++);
            waitSerialPortEvent(blockTimeout);
        }
        inputStream.read(bytes);
    }

    public void read(byte[] bytes, int offset, int length, int blockTimeout) throws Exception {
        byte[] clone = bytes.clone();
        read(clone, blockTimeout);
    }

    public byte readByte(int blockTimeout) throws Exception {
        byte[] buffer = new byte[1];
        read(buffer, blockTimeout);
        return buffer[0];
    }

    public short readHalf(int blockTimeout) throws Exception {
        byte[] buffer = new byte[2];
        read(buffer, blockTimeout);
        short ret = (short)(buffer[0] | buffer[1]<<8);
        return ret;
    }

    public int readWord(int blockTimeout) throws Exception {
        byte[] buffer = new byte[4];
        read(buffer, blockTimeout);
        int ret = buffer[0] | buffer[1]<<8 | buffer[2]<<16 | buffer[3]<<24;
        return ret;
    }
}
